package tsif.tcluster;

import java.util.ArrayList;
import java.util.List;

import tsif.tcluster.TClusterMaster.ContextNode;
import tsif.tcluster.master.TChlusterLocalMaster;
import tsif.tcluster.master.TChlusterRemoteMaster;
import tsif.tcluster.service.ITClusterService;
import tsif.tcluster.service.TClusterService;
import tsif.tcluster.service.proxy.TClusterServiceProxy;
import tsif.tcluster.service.proxy.TClusterServices;
import tsif.tcluster.service.proxy.impl.TClusterRpcServiceProxy;
import tsif.tcluster.service.proxy.impl.TClusterRpcServiceProxyByLoader;
import tsif.tcluster.session.TClusterSession;
import tsif.tcluster.session.TClusterSessions;

/**
 * 集群环境(核心管理对象)<br>
 * 1. 管理集群下的本机服务<br>
 * 2. 管理各个集群访问的session<br>
 * 3. 管理session的访问回调<br>
 */
public class TClusterContext {
	protected boolean running; // 是否运行中

	protected boolean useServiceClassLoader; // 是否给服务配置独立的加载类
	protected String host; // 本机地址(提供给其他分支访问)
	protected int port; // 本地端口

	protected int contextId; // 实例Id, 集群环境唯一
	protected long localServiceId; // 本地服务Id

	protected TClusterMaster master; // 中央控制
	protected ITClusterAccessor accessor; // 本地服务

	protected TClusterServices services; // 服务管理器
	protected TClusterSessions sessions; // 其他节点连接表

	public TClusterContext() {
		this(false);
	}

	/**
	 * @param useServiceClassLoader
	 *            是否给服务配置独立的加载类
	 */
	public TClusterContext(boolean useServiceClassLoader) {
		sessions = new TClusterSessions(this);
		services = new TClusterServices(this);

		this.useServiceClassLoader = useServiceClassLoader;
		running = false;
	}

	/** 以本机作为中央服启动 **/
	public boolean start(String host, int port) {
		return start(0, host, port);
	}

	/** 设置中央服地址启动(中央服地址不填为本地) **/
	public boolean start(String host, int port, String mhost, int mport) {
		return start(0, host, port, mhost, mport);
	}

	/** 以本机作为中央服启动, 可指定集群Id(可用于重启) **/
	public boolean start(int contextId, String host, int port) {
		return init(contextId, host, port, host, port);
	}

	/** 设置中央服地址启动(中央服地址不填为本地), 可指定集群Id(可用于重启) **/
	public boolean start(int contextId, String host, int port, String mhost, int mport) {
		return init(contextId, host, port, mhost, mport);
	}

	/**
	 * 启动初始化<br>
	 * 
	 * @param contextId
	 *            环境Id, 0为自动分配, 可指定<br>
	 * @param host
	 *            本机地址
	 * @param port
	 *            本机端口
	 * @param mhost
	 *            中央控制地址(本机作为中控填本机)
	 * @param mport
	 *            中央控制端口(本机作为中控填本机)
	 * 
	 **/
	protected boolean init(int contextId, String host, int port, String mhost, int mport) {
		boolean success = false;
		try {
			// check master params
			if (mport == 0 || mhost == null || mhost.length() <= 0) {
				throw new RuntimeException("master no params! mhost=" + mhost + " mport=" + mport);
			}

			// create master
			TClusterServiceProxy masterProxy = null;
			if (port == mport && host.equals(mhost)) {
				// local master
				masterProxy = createAndInitServiceProxy(TClusterMaster.MASTER_SERVICE_ID, null, TChlusterLocalMaster.class);
			} else {
				// create remote master
				masterProxy = createAndInitServiceProxy(TClusterMaster.MASTER_SERVICE_ID, null, TChlusterRemoteMaster.class, mhost, mport);
			}
			this.master = masterProxy.getService();

			// 如果本机不是主机
			if (!this.isMaster()) {
				// 尝试连接
				if (!((TChlusterRemoteMaster) master).tryToConnect(5 * 1000, 500)) {
					throw new RuntimeException("remote master connect error! " + mhost + " " + mport);
				}
			}

			// register by master
			this.host = host;
			this.port = port;
			this.contextId = master.registerContext(contextId, host, port);
			if (this.contextId == 0) {
				throw new RuntimeException("register context fail!");
			}
			localServiceId = TClusterServices.getLocalServiceId(this.contextId);
			// create context service
			TClusterServiceProxy localProxy = createAndInitServiceProxy(TClusterMaster.LOCAL_SERVICE_ID, null, createLocalServiceClass());
			accessor = localProxy.getAccessor();

			success = true;
			running = true;

			// update frist
			update();

			return success;
		} catch (Exception e) {
			System.err.println("init error! " + host + " " + port);
			e.printStackTrace();
			return success;
		} finally {
			if (!success) {
				// shutdown
				try {
					shutdown();
				} catch (Exception e) {
					throw new RuntimeException(e);
				}
			}
		}
	}

	/** 创建本机服务(可实现派生) **/
	protected Class<?> createLocalServiceClass() {
		return TClusterLocalService.class;
	}

	/** 创建本地连接 **/
	protected TClusterSession createLocalSession() {
		return new TChlusterLocalSession(this);
	}

	/** 服务节点创建 **/
	protected TClusterServiceProxy createServiceProxy(long serviceId, String uniqueName, Class<?> classValue, Object... args) throws Exception {
		// 使用类加载器
		if (useServiceClassLoader) {
			return TClusterRpcServiceProxyByLoader.createServiceProxy(serviceId, uniqueName, classValue, args);
		}
		return new TClusterRpcServiceProxy();
	}

	/** 创建远程连接session **/
	protected TClusterSession createSession(int contextId, String host, int port) {
		throw new RuntimeException("create session error! contextId=" + contextId + " host=" + host + " port=" + port);
	}

	/** 获取服务对应的连接会话 **/
	protected TClusterSession getSession(Object service) {
		long serviceId = this.getServiceId(service);
		int contextId = this.getContextId(serviceId);
		return getSession(contextId);
	}

	/** 获取集群session, 如果不存在就创建新的 **/
	protected TClusterSession getSession(int contextId) {
		// get session
		TClusterSession session = sessions.get(contextId);
		if (session != null) {
			return session;
		}

		// 上锁, 避免创建多个连接
		synchronized (sessions) {
			// 再次获取
			session = this.sessions.get(contextId);
			if (session != null) {
				return session;
			}

			// create session
			long selfContextId = this.getContextId();
			if (contextId == TClusterMaster.MASTER_CONTEXT_ID) {
				// check
				if (isMaster()) {
					throw new RuntimeException("don't has remote master! selfContextId=" + selfContextId);
				}
				TChlusterRemoteMaster rmaster = ((TChlusterRemoteMaster) getMaster());
				return getOrCreateSession(contextId, rmaster.getHost(), rmaster.getPort());
			} else if (contextId == selfContextId) {
				// create local session
				session = createLocalSession();
				session = sessions.tryToAddSession(this.contextId, session);
				return session;
			}

			// read context node
			TClusterMaster master = getMaster();
			ContextNode contextNode = (master != null) ? master.getContext(contextId) : null;
			if (contextNode == null) {
				throw new RuntimeException("no find context by: contextId=" + contextId + " selfContextId=" + selfContextId);
			}
			return getOrCreateSession(contextId, contextNode.getHost(), contextNode.getPort());
		}
	}

	/** 获取或者创建远程session **/
	protected TClusterSession getOrCreateSession(int contextId, String host, int port) {
		TClusterSession session = createSession(contextId, host, port);
		return sessions.tryToAddSession(contextId, session);
	}

	/** 本机是否是主机 **/
	public boolean isMaster() {
		return (master instanceof TChlusterLocalMaster);
	}

	/** 停止 **/
	public void stop() {
		shutdown();

		// remove local service
		services.remove(TClusterMaster.LOCAL_SERVICE_ID);

		// remove master
		if (master != null) {
			// destroy master
			try {
				services.remove(TClusterMaster.MASTER_SERVICE_ID);
			} catch (Exception e) {
				System.err.println("destroy master error! " + e.toString());
				e.printStackTrace();
			}
			master = null;
		}

		// remove session
		sessions.removeAll();
	}

	/** 内核关闭停止(关闭本身的其他服务) **/
	protected void shutdown() {
		// System.out.println("shutdown [" + this.contextId + " " + host + ":" + port + "]");
		running = false;

		// shutdown service
		List<Long> serviceIds = new ArrayList<>(services.getServiceIds());
		for (long serviceId : serviceIds) {
			if (isBaseServiceId(serviceId)) {
				continue; // 暂时保留不移除
			}
			services.remove(serviceId);
		}

		// remove master
		if (master != null) {
			// remove context
			try {
				master.removeContext(contextId);
			} catch (Exception e) {
				System.err.println("remove context error! " + e.toString());
				// e.printStackTrace();
			}
		}

	}

	/** 是否运行中 **/
	public boolean isRunning() {
		return running;
	}

	/**
	 * 创建服务<br>
	 * 
	 * @param classValue
	 *            类(可以是名称, 也可以类对象)
	 * @param args
	 *            初始化参数
	 **/
	public long newService(Class<?> classValue, Object... args) throws Exception {
		return newService(newServiceId(), classValue, args);
	}

	/**
	 * 创建服务<br>
	 * 
	 * @param serviceId
	 *            服务Id(0为自动分配, 可指定)
	 * @param classValue
	 *            类(可以是名称, 也可以类对象)
	 * @param args
	 *            初始化参数
	 **/
	public long newService(long serviceId, Class<?> classValue, Object... args) throws Exception {
		createAndInitServiceProxy(serviceId, null, classValue, args);
		return serviceId;
	}

	/** 获取集群唯一服务Id **/
	public long getUniqueServiceId(Object key0) {
		String key = key0.toString();
		return this.getMaster().getUniqueService(key);
	}

	/**
	 * 注册或获取集群唯一服务<br>
	 * 
	 * @param key
	 *            唯一服务Key
	 * @param classValue
	 *            类(可以是名称, 也可以类对象)
	 * @param args
	 *            初始化参数
	 **/
	public long uniqueService(Object key, Class<?> classValue, Object... args) throws Exception {
		return uniqueService(newServiceId(), key, false, classValue, args);
	}

	/**
	 * 注册或获取集群唯一服务<br>
	 * 
	 * @param newServiceId
	 *            服务Id(只有创建才生效, 如果已存在唯一服务, 这个Id无效. 0为自动分配, 可指定)
	 * @param key
	 *            唯一服务Key
	 * 
	 * @param replace
	 *            替换创建全局唯一服务
	 * @param classValue
	 *            类(可以是名称, 也可以类对象)
	 * @param args
	 *            初始化参数
	 **/
	public long uniqueService(long newServiceId, Object key, boolean replace, Class<?> classValue, Object... args) throws Exception {
		String uniqueName = key.toString();

		// 绑定服务(有可能原本存在的服务Id就是当前的, 说明当前的重启重新加载了.)
		long nowServiceId = master.bindUniqueService(uniqueName, newServiceId, replace);
		if (newServiceId != nowServiceId) {
			return nowServiceId; // 绑定失败, 其他服务注册了, 返回当前全局服务Id
		}

		// 创建服务(服务Id相同也执行重载)
		createAndInitServiceProxy(newServiceId, uniqueName, classValue, args);
		return newServiceId;
	}

	/** 是否是基础服务Id **/
	private boolean isBaseServiceId(long serviceId) {
		return serviceId == TClusterMaster.MASTER_SERVICE_ID //
				|| serviceId == TClusterMaster.LOCAL_SERVICE_ID //
				|| serviceId == localServiceId //
		;
	}

	/** 创建和初始化服务节点(内部调用) **/
	protected TClusterServiceProxy createAndInitServiceProxy(long serviceId, String uniqueName, Class<?> classValue, Object... args) throws Exception {
		// 检测是否是本地服务(本地服务不加类加载器)
		TClusterServiceProxy serviceProxy = null;
		if (isBaseServiceId(serviceId)) {
			serviceProxy = new TClusterRpcServiceProxy(); // 核心系统服务, 直接创建.
		} else {
			serviceProxy = this.createServiceProxy(serviceId, uniqueName, classValue, args);
		}

		// 判断是否用不同的类加载器
		if (!useServiceClassLoader) {
			// 检测类加载器
			if (!serviceProxy.getClass().getClassLoader().equals(this.getClass().getClassLoader())) {
				throw new RuntimeException("can't use other classLoader! serviceProxy=" + serviceProxy + " classLoader=" + serviceProxy.getClass().getClassLoader());
			}
		}

		return services.addAndInitServiceProxy(serviceProxy, serviceId, uniqueName, classValue, args);
	}

	/**
	 * 发送消息到指定服务<br>
	 **/
	public void send(long sourceServiceId, Object targetService, short code, Object data) {
		try {
			long targetServiceId = getServiceId(targetService);
			if (targetServiceId == 0) {
				throw new RuntimeException("empty target serviceId! ");
			}
			// send
			TClusterSession session = getSession(targetServiceId);
			session.send(sourceServiceId, targetServiceId, code, data);
		} catch (RuntimeException e) {
			throw e;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/**
	 * 接受消息交给指定服务处理(这里提交线程池没意义, 应该是call和callback其中一个提交线程)<br>
	 **/
	public void recv(TClusterSession session, long sourceServiceId, long targetServiceId, short code, Object data) throws Exception {
		// init target serviceId
		if (targetServiceId == localServiceId) {
			targetServiceId = TClusterMaster.LOCAL_SERVICE_ID; // 本地服务标记转化
		}
		// get service
		TClusterServiceProxy serviceProxy = services.get(targetServiceId);
		if (serviceProxy == null) {
			throw new RuntimeException("not find service! serviceId=" + targetServiceId + " sourceServiceId=" + sourceServiceId + " code=" + code);
		}
		// dispose service
		serviceProxy.recv(session, sourceServiceId, code, data);
	}

	/** 更新处理(超时更新) **/
	public void update() {
		sessions.update();
		services.update();

		// update context(不定时清除, 也不必更新)
		// if (master != null) {
		// master.updateContext(contextId);
		// }
	}

	/**
	 * 根据Key识别服务Id<br>
	 * 
	 * @param service
	 *            服务Key, 可以是名称可以是Id.
	 **/
	protected long getServiceId(Object service) {
		// instanceof 比 isInstance好
		if (service instanceof Long) {
			return (Long) service;
		} else if (service instanceof Integer) {
			return (Integer) service;
		} else if (service instanceof String) {
			return this.getMaster().getUniqueService((String) service);
		}
		throw new RuntimeException("service key error! " + service);
	}

	/** 通过服务器Id获取设备Id **/
	public int getContextId(long serviceId) {
		if (serviceId == TClusterMaster.MASTER_SERVICE_ID) {
			return 0;
		} else if (serviceId == TClusterMaster.LOCAL_SERVICE_ID || serviceId == localServiceId) {
			return this.contextId;
		}
		return TClusterServices.getContextId(serviceId);
	}

	/** 判断是否是本地服务Id **/
	public boolean isLocalService(Object service) {
		return isLocalServiceId(getServiceId(service));
	}

	/** 判断是否是本地服务Id **/
	public boolean isLocalServiceId(long serviceId) {
		int contextId = getContextId(serviceId);
		return contextId == this.contextId;
	}

	/** 遍历执行服务 **/
	public void forech(IServiceAction action) {
		services.forech(action);
	}

	public final String getHost() {
		return host;
	}

	public final int getPort() {
		return port;
	}

	public final int getContextId() {
		return contextId;
	}

	/** 获取中央控制对象 **/
	public TClusterMaster getMaster() {
		return master;
	}

	/** 获取一个服务Id(集群唯一/本机唯一) **/
	public long newServiceId() {
		return services.newServiceId();
	}

	/** 获取本地服务访问器 **/
	public ITClusterAccessor getAccessor() {
		return accessor;
	}

	/** 本地服务是否使用独立的类加载器 **/
	public boolean isUseServiceClassLoader() {
		return useServiceClassLoader;
	}

	@Override
	public String toString() {
		return "TClusterContext [contextId=" + contextId + ", host=" + host + ", port=" + port + "]";
	}

	/** 服务遍历接口 **/
	public interface IServiceAction {
		void action(TClusterServiceProxy serviceProxy, ITClusterService service) throws Exception;
	}

	/** 本机连接 **/
	protected class TChlusterLocalSession extends TClusterSession {
		public TChlusterLocalSession(TClusterContext context) {
			super(context, context.getContextId());
			this.context = context;
		}

		@Override
		public void close() {
		}

		@Override
		public boolean alive() {
			return true; // 永久有效
		}

		@Override
		public void send(long sourceServiceId, long targetServiceId, short code, Object data) {
			try {
				this.recv(sourceServiceId, targetServiceId, code, data);
			} catch (RuntimeException e) {
				throw e;
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}

	}

	/**
	 * 集群环境本地服务<br>
	 * 1. 处理关闭功能<br>
	 * 2. 用于派生实现其他功能<br>
	 **/
	public static class TClusterLocalService extends TClusterService {

		@Override
		public boolean init(TClusterServiceProxy serviceProxy, long serviceId, Object... args) throws Exception {
			if (!super.init(serviceProxy, serviceId, args)) {
				return false;
			}
			return true;
		}

		@Override
		public void destroy() throws Exception {
		}

		@Override
		public void dispose(long serviceId, String funcName, Object[] args, IReturn<Object> ret) throws Exception {
			if (funcName.equals("shutdown")) {
				ret.ret(1, null, shutdown((int) args[0]));
				return;
			}
		}

		protected boolean shutdown(int shutdownTime) {
			// check running
			if (!context.isRunning()) {
				return true;
			}

			// shutdown
			context.shutdown();

			// abort
			if (context.isMaster()) {
				context.getMaster().abort(shutdownTime); // 会嵌套调用
				return true;
			}

			return true;
		}

		@Override
		public void update() {
		}

		@Override
		public String toString() {
			return "TClusterLocalService [context=" + context + "]";
		}

	}
}
